package hu.elte.inf.holiuml.gui;

import hu.elte.inf.holiuml.model.ClassDiagram;
import hu.elte.inf.holiuml.model.CollaborationDiagram;
import hu.elte.inf.holiuml.model.Diagram;
import hu.elte.inf.holiuml.model.Document;
import hu.elte.inf.holiuml.fileutil.CodeGenerator;
import hu.elte.inf.holiuml.fileutil.PersistenceService;
import hu.elte.inf.holiuml.fileutil.FileUtilFactory;

import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;

/**
 * @author Csaba Hoch
 * @author Istvan Bozo
 */
public class HoliUML extends JFrame {

	private Document document = new Document();
	private Canvas canvas = new Canvas(this);
	private JMenu diagramMenu = null;
	private JMenu diagramsMenu;
	private JMenu fileMenu;
	private boolean modified = false;
	JMenuBar menuBar = new JMenuBar();
	PersistenceService persistenceService;
	CodeGenerator codeGenerator;

	public HoliUML() {
		super("HoliUML");
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});

		addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				canvas.keyPressed(e);
			}

			public void keyTyped(KeyEvent e) {
				canvas.keyTyped(e);
			}
		});

//		CollaborationDiagram collaborationDiagram = new CollaborationDiagram();
//		document.addCollaborationDiagram(collaborationDiagram);
		
		createMenuBar();
		add(canvas);
		pack();
		setVisible(true);
	}

	private void createDiagramMenu(final Diagram diagram){
		final JMenu menu = new JMenu();
		diagram.setMain(HoliUML.this);
		diagram.createMenu(menu);
		newDiagramMenu(menu);
		canvas.setDiagram(diagram);
		diagramsMenu.add(new JMenuItem(new AbstractAction(diagram.getName()) {
			public void actionPerformed(ActionEvent e) {
				canvas.setDiagram(diagram);
				newDiagramMenu(menu);
			}
		}));
	}
	private void createMenuBar() {
		//"File" menu
		fileMenu = new JMenu("File");
		menuBar.add(fileMenu);
		try{
			persistenceService = FileUtilFactory.getXMLPersister();
			codeGenerator = FileUtilFactory.getJavaGenerator();
		}catch(Exception exception){
			JOptionPane.showMessageDialog(HoliUML.this, "An error has occured while creating menu!");
			System.exit(0);
		}
		fileMenu.add(new JMenuItem(new AbstractAction("Open") {
			public void actionPerformed(ActionEvent e) {
				try{
					document = persistenceService.open(HoliUML.this);
					if(document != null){
						Vector<ClassDiagram> classDiagrams = document.getClassDiagram();
						Vector<CollaborationDiagram> collaborationDiagrams = document.getCollaborationDiagram();
						for(int i = 0; i < classDiagrams.size(); i++){
							createDiagramMenu(classDiagrams.get(i));
						}
						for(int i = 0; i < collaborationDiagrams.size(); i++){
							createDiagramMenu(collaborationDiagrams.get(i));
						}
					}
				}catch(Exception exception){
					JOptionPane.showMessageDialog(HoliUML.this, "An error has occured while opening the project!", "Error!", JOptionPane.ERROR_MESSAGE); 
				}
				canvas.repaint();
			}
		}));
		fileMenu.add(new JMenuItem(new AbstractAction("Save") {
			public void actionPerformed(ActionEvent e) {
				try{
					persistenceService.save(document, HoliUML.this);
				}catch(Exception exception){
					JOptionPane.showMessageDialog(HoliUML.this, "An error has occured while saving the project!", "Error!", JOptionPane.ERROR_MESSAGE);
				}
			}
		}));		
		fileMenu.add(new JMenuItem(new AbstractAction("Save As") {
			public void actionPerformed(ActionEvent e) {
				try{
					persistenceService.saveAs(document, HoliUML.this);
				}catch(Exception exception){
					JOptionPane.showMessageDialog(HoliUML.this, "An error has occured while saving the project!", "Error!", JOptionPane.ERROR_MESSAGE);
				}
			}
		}));
		fileMenu.add(new JMenuItem(new AbstractAction("Generate Code") {
			public void actionPerformed(ActionEvent e) {
				if(document.getClassDiagram().size() != 0){
					try{
						CodeGeneratorOptionsDialog optDlg = new CodeGeneratorOptionsDialog(HoliUML.this, "Choose a class!", document.getClassDiagram());
						if (optDlg.getButtonCode() == OptionsDialog.OK) {
							ClassDiagram classDiagram = optDlg.getClassDiagram();
							if(classDiagram == null){
								JOptionPane.showMessageDialog(HoliUML.this, "There is no class diagram!", "Error!", JOptionPane.ERROR_MESSAGE);
							}else{
								codeGenerator.generate(classDiagram, HoliUML.this);
							}
						}
					}catch(Exception exception){
						JOptionPane.showMessageDialog(HoliUML.this, "An error has occured while generating code!", "Error!", JOptionPane.ERROR_MESSAGE);
					}
				}else{
					JOptionPane.showMessageDialog(HoliUML.this, "There is no existing document or there is no class diagram in the current document!", "Error!", JOptionPane.ERROR_MESSAGE);
				}
			}
		}));
		fileMenu.add(new JMenuItem(new AbstractAction("Exit") {
			public void actionPerformed(ActionEvent e) {
				if (isModified()){
					int choise = JOptionPane.showConfirmDialog(HoliUML.this,"Would you like to save the project before exit?", "Unsaved Project", JOptionPane.YES_NO_OPTION);
					if(choise == 0){
						try{
							persistenceService.save(document, HoliUML.this);
						}catch(Exception exception){
							JOptionPane.showMessageDialog(HoliUML.this, "An error has occured while saving the project!", "Error!", JOptionPane.ERROR_MESSAGE);
						}
					}
				}
				System.exit(0);
			}
		}));
		
		// "Diagrams" menu
		diagramsMenu = new JMenu("Diagrams");
		
		//"New class diagram" menu item
		diagramsMenu.add(new JMenuItem(new AbstractAction("New class diagram") {
			public void actionPerformed(ActionEvent e) {
				String diagramName = JOptionPane.showInputDialog(HoliUML.this, "Please enter the name of the new diagam:", "Class diagram");
				if(diagramName != null){
					final ClassDiagram classDiagram = new ClassDiagram();
					classDiagram.setMain(HoliUML.this);
					document.addClassDiagram(classDiagram);
					final JMenu menu = new JMenu();
					classDiagram.createMenu(menu);
					canvas.setDiagram(classDiagram);
					newDiagramMenu(menu);
					classDiagram.setName(diagramName);
					diagramsMenu.add(new JMenuItem(new AbstractAction(diagramName) {
						public void actionPerformed(ActionEvent e) {
							canvas.setDiagram(classDiagram);
							newDiagramMenu(menu);
						}
					}));
				}
			}
		}));

		//"New collab diagram" menu item
		diagramsMenu.add(new JMenuItem(new AbstractAction("New collaboration diagram") {
			public void actionPerformed(ActionEvent e) {
				String diagramName = JOptionPane.showInputDialog(HoliUML.this, "Please enter the name of the new diagam:", "Collaboration diagram");
				if(diagramName != null){
					final CollaborationDiagram collabDiagram = new CollaborationDiagram();
					collabDiagram.setMain(HoliUML.this);
					document.addCollaborationDiagram(collabDiagram);
					final JMenu menu = new JMenu();
					collabDiagram.createMenu(menu);
					canvas.setDiagram(collabDiagram);
					newDiagramMenu(menu);
					collabDiagram.setName(diagramName);
					diagramsMenu.add(new JMenuItem(new AbstractAction(diagramName) {
						public void actionPerformed(ActionEvent e) {
							canvas.setDiagram(collabDiagram);
							newDiagramMenu(menu);
						}
					}));
				}
			}
		}));
		
		diagramsMenu.addSeparator();
		
		menuBar.add(diagramsMenu);
		setJMenuBar(menuBar);
	}
	
	public Canvas getCanvas() {
		return canvas;
	}

	public Document getDocument() {
		return document;
	}
	
	private void newDiagramMenu(JMenu menu) {
		if (diagramMenu != null) {
			menuBar.remove(diagramMenu);
		}
		menuBar.add(menu);
		diagramMenu = menu;
		setJMenuBar(menuBar);
	}
	
	private static HoliUML main;
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		main = new HoliUML();
	}

	public void setModified(boolean modified) {
		this.modified = modified;
	}

	public boolean isModified() {
		return modified;
	}

	public static HoliUML getMain() {
		return main;
	}

}
